using System;
using System.Web.UI.WebControls;
using System.Web.UI;
using System.Reflection;
using System.Collections;
using System.Data;
using Generic.Collections;
using System.Collections.Generic;
using CodeWork.Library.Data;

namespace Generic.WebControls
{
	#region Adapters

	public interface IDataSourceAdapter
	{
		int TotalCount{get;}
		object GetPagedData(int start,int end);
	}
	internal class DataViewAdapter:IDataSourceAdapter
	{
		private DataView _view;

		internal DataViewAdapter(DataView view)
		{
			_view = view;
		}
		public int TotalCount
		{
			get{return (_view == null) ? 0 : _view.Table.Rows.Count;}
		}
		public object GetPagedData(int start, int end)
		{
			DataTable table = _view.Table.Clone();

			for (int i = start;i<=end && i<= TotalCount;i++)
			{
				table.ImportRow(_view[i-1].Row);
			}
			return table;
		}
	}

	#endregion
	
	# region Factory classes

	public abstract class AdapterBuilder
	{
		private object _source;

		private void CheckForNull()
		{
			if (_source == null) throw new NullReferenceException("You must provide a valid source");
		}
		public virtual object Source
		{
			get
			{
				CheckForNull(); 
				return _source;}
			set
			{
				_source = value;
				CheckForNull();
			}
		}
		public abstract IDataSourceAdapter Adapter{get;}
	}
	public class DataTableAdapterBuilder:AdapterBuilder
	{
		private DataViewAdapter _adapter;

		private DataViewAdapter ViewAdapter
		{
			get
			{
				if (_adapter == null)
				{
					DataTable table = (DataTable)Source;
					_adapter = new DataViewAdapter(table.DefaultView);
				}
				return _adapter;
			}
		}
		public override IDataSourceAdapter Adapter
		{
			get
			{
				return ViewAdapter;
			}
		}
	}
	public class DataViewAdapterBuilder:AdapterBuilder
	{
		private DataViewAdapter _adapter;

		private DataViewAdapter ViewAdapter
		{
			get
			{   //lazy instantiate
				if (_adapter == null)
				{
					_adapter = new DataViewAdapter((DataView)Source);
				}
				return _adapter;
			}
		}
		public override IDataSourceAdapter Adapter
		{
			get{return ViewAdapter;}
		}
	}

	#endregion

	#region Templates

	public class DefaultPagerLayout:ITemplate
	{
		private ImageButton Next;
		private ImageButton First;
		private ImageButton Last;
		private ImageButton Previous;
		private Panel Pager;
		
		public DefaultPagerLayout()
		{
			Next = new ImageButton();
			First = new ImageButton();
			Last = new ImageButton();
			Previous = new ImageButton();
			Pager = new Panel();

			Next.ID="Next"; Next.AlternateText="Next";Next.ImageUrl="play2.gif"; 
			First.ID="First"; First.AlternateText="First";First.ImageUrl="play2L_dis.gif";
			Last.ID = "Last"; Last.AlternateText ="Last";Last.ImageUrl="play2_dis.gif";
			Previous.ID="Previous"; Previous.AlternateText="Previous";Previous.ImageUrl="play2L.gif";
			Pager.ID="Pager";
		}
		public void InstantiateIn(Control control)
		{
			control.Controls.Clear();
			Table table = new Table();
			table.BorderWidth = Unit.Pixel(0);
			table.CellSpacing= 1;
			table.CellPadding =0;
			TableRow row = new TableRow();
			row.VerticalAlign = VerticalAlign.Top;
			table.Rows.Add(row);
			TableCell cell = new TableCell();
			cell.HorizontalAlign = HorizontalAlign.Right;
			cell.VerticalAlign = VerticalAlign.Middle;
			cell.Controls.Add(First);
			cell.Controls.Add(Previous);
			row.Cells.Add(cell);
			cell = new TableCell();
			cell.HorizontalAlign= HorizontalAlign.Center;
			cell.Controls.Add(Pager);
			row.Cells.Add(cell);
			cell = new TableCell();
			cell.VerticalAlign = VerticalAlign.Middle;
			cell.Controls.Add(Next);
			cell.Controls.Add(Last);
			row.Cells.Add(cell);

			control.Controls.Add(table);
		}
	}
	public class LayoutContainer:Control,INamingContainer
	{
		public LayoutContainer()
		{this.ID = "Page";}
	}
	#endregion

	#region Events

	public delegate void PageDelegate(object sender,PageChangedEventArgs e);
	public enum PagedEventInvoker{Next,Previous,First,Last,Pager}
	public class PageChangedEventArgs:EventArgs
	{
		private int newpage;
		private Enum invoker;

		public PageChangedEventArgs(int newpage):base()
		{
			this.newpage = newpage;
		}
		public PageChangedEventArgs(int newpage,PagedEventInvoker invoker)
		{
			this.newpage = newpage;
			this.invoker = invoker;
		}
		public int NewPage {get{return newpage;}}
		public Enum EventInvoker{get{return invoker;}}
	}
	#endregion

	#region Pager

    public class Pager:WebControl,INamingContainer
	{
		protected System.Web.UI.WebControls.ImageButton First;
		protected System.Web.UI.WebControls.ImageButton Last;
		protected System.Web.UI.WebControls.ImageButton Next;
		protected System.Web.UI.WebControls.ImageButton Previous;
		protected System.Web.UI.WebControls.Style UnselectedPager;
		protected System.Web.UI.WebControls.Style SelectedPager;
		protected System.Web.UI.WebControls.Label PageNum;
		protected System.Web.UI.WebControls.Panel Holder;
		private Control _boundcontrol;
		private string _bindcontrol;
		private int _results = 10;
		private int _resultsperpage = 10;
		private PropertyInfo _datasource;
		private ITemplate _layout;
		private LayoutContainer _container;
		private bool _isdatasensitive=true;
		private AdapterCollection _adapters;
		private AdapterBuilder _builder;
		public event PageDelegate PageChanged;
		public event EventHandler DataUpdate;


		public Pager()
		{
			SelectedPager=new System.Web.UI.WebControls.Style();
			UnselectedPager = new System.Web.UI.WebControls.Style();
			_adapters = new AdapterCollection();
			_adapters.Add(typeof(DataTable),new DataTableAdapterBuilder());
			_adapters.Add(typeof(DataView),new DataViewAdapterBuilder());
		}
		protected override void OnInit(EventArgs e)
		{
			_boundcontrol = Parent.FindControl(BindToControl);
			BoundControl.DataBinding += new EventHandler(BoundControl_DataBound);
			InstantiateTemplate();
			Controls.Add(_container);
			base.OnInit(e);
		}
		[TemplateContainer(typeof(LayoutContainer))]
		public ITemplate Layout
		{
			get{return (_layout == null)? new DefaultPagerLayout():_layout;}
			set{_layout =value;}
		}
		public AdapterCollection Adapters
		{
			get{return _adapters;}
		}
		public bool IsDataSensitive
		{
			get{return _isdatasensitive;}
			set{_isdatasensitive = value;}
		}
		public int CurrentPage
		{
			get
			{
				string cur = (string)ViewState["CurrentPage"];
				return (cur == string.Empty || cur ==null)? 1 : int.Parse(cur);
			}
			set
			{
				ViewState["CurrentPage"] = value.ToString();}
		}
		public string BindToControl
		{
			get
			{
				if (_bindcontrol == null) 
					throw new NullReferenceException("You must bind to a control through the BindToControl property before you use the pager");
				return _bindcontrol;}
			set{_bindcontrol=value;}
		}
		private ImageButton FirstButton{get {return First;}}
		private ImageButton LastButton{get {return Last;}}
		private ImageButton PreviousButton{get {return Previous;}}
		private ImageButton NextButton{get {return Next;}}
		public Style UnSelectedPagerStyle {get {return UnselectedPager;}}
		public Style SelectedPagerStyle {get {return SelectedPager;}}
		public Label PageNumberLabel {get {return PageNum;}}

		public int PagersToShow
		{
			get{return _results;}
			set{_results = value;}
		}
		public int TotalResults
		{
			get{return _builder.Adapter.TotalCount;}
		}
		public int ResultsToShow
		{
			get{return _resultsperpage;}
			set{_resultsperpage = value;}
		}
		private int TotalPagesToShow
		{
			get{return Convert.ToInt32(Math.Ceiling((double)TotalResults/(double)_resultsperpage));}
		}
		private int StartRow
		{
			get{return ResultsToShow*(CurrentPage-1)+1;}
		}
		private Control BoundControl
		{
			get{return _boundcontrol;}
		}
		private void GeneratePagers()
		{
			GeneratePagers(Holder);
		}
		private int PagerSequence
		{
			get
			{
				return Convert.ToInt32
					(Math.Ceiling((double)CurrentPage/(double)PagersToShow));}
		}
		private int NumberOfPagersToGenerate
		{
			get{return PagerSequence*PagersToShow;}
		}
		private bool IsPagerVisible
		{
			get{return (TotalPagesToShow != 1) && IsDataSensitive;}
		}
		private bool IsPreviousVisible
		{
			get
			{
				return (!IsDataSensitive)? true:
					(CurrentPage != 1);
			}
		}
		private bool IsNextVisible
		{
			get
			{
				return (!IsDataSensitive)? true:
					(CurrentPage != TotalPagesToShow);
			}
		}
		private void ApplyDataSensitivityRules()
		{
			FirstButton.Visible = IsPreviousVisible;
			PreviousButton.Visible = IsPreviousVisible;
			LastButton.Visible = IsNextVisible;
			NextButton.Visible = IsNextVisible;
			if (IsPagerVisible) GeneratePagers();
		}
		private void GeneratePagers(WebControl control)
		{
			control.Controls.Clear();
			int pager = (PagerSequence-1)* PagersToShow +1;

			for (;pager<=NumberOfPagersToGenerate && pager<=TotalPagesToShow;pager++)
			{
				LinkButton link = new LinkButton();
				link.Text = pager.ToString();
				link.ID = pager.ToString();
				link.Click += new EventHandler(this.Pager_Click);
				if (link.ID.Equals(CurrentPage.ToString()))
					link.MergeStyle(SelectedPagerStyle);
				else
					link.MergeStyle(UnSelectedPagerStyle);

				control.Controls.Add(link);
				control.Controls.Add(new LiteralControl("&nbsp;"));
			}
		}
		private void RaiseEvent(EventHandler e,object sender )
		{
			this.RaiseEvent(e,this,null);
		}
		private void RaiseEvent(EventHandler e,object sender,PageChangedEventArgs args)
		{
			if(e!=null) 
			{
				e(sender,args);
			}
		}
		private void RaiseEvent(PageDelegate e,object sender )
		{
			this.RaiseEvent(e,this,null);
		}
		private void RaiseEvent(PageDelegate e,object sender,PageChangedEventArgs args)
		{
			if(e!=null) 
			{
				e(sender,args);
			}
		}
		private void BindParent()
		{
			_datasource.GetSetMethod().Invoke(BoundControl,
				new object[]{_builder.Adapter.GetPagedData(StartRow,ResultsToShow*CurrentPage)});

		}
		private bool HasParentControlCalledDataBinding
		{
			get{return _builder != null;}
		}
		private void BoundControl_DataBound(object sender,System.EventArgs e)
		{
			if (HasParentControlCalledDataBinding) return;
			Type type = sender.GetType();
			_datasource = type.GetProperty("DataSource");
			if (_datasource == null)
				throw new NotSupportedException("The Pager control doesn't support controls that don't contain a datasource");
			object data = _datasource.GetGetMethod().Invoke(sender,null);
			_builder = Adapters[data.GetType()];
			if (_builder == null) 
				throw new NullReferenceException("There is no adapter installed to handle a datasource of type "+data.GetType()); 
			_builder.Source = data;

			ApplyDataSensitivityRules();
			BindParent();
			RaiseEvent(DataUpdate,this);
		}
		private void InstantiateTemplate()
		{
			_container = new LayoutContainer();
			Layout.InstantiateIn(_container);
			First = (ImageButton)_container.FindControl("First");
			Previous = (ImageButton)_container.FindControl("Previous");
			Next = (ImageButton)_container.FindControl("Next");
			Last = (ImageButton)_container.FindControl("Last");
			Holder = (Panel)_container.FindControl("Pager");
			this.First.Click += new System.Web.UI.ImageClickEventHandler(this.First_Click);
			this.Last.Click += new System.Web.UI.ImageClickEventHandler(this.Last_Click);
			this.Next.Click += new System.Web.UI.ImageClickEventHandler(this.Next_Click);
			this.Previous.Click += new System.Web.UI.ImageClickEventHandler(this.Previous_Click);
		}
		private void Update()
		{
			if (!HasParentControlCalledDataBinding) return;
			ApplyDataSensitivityRules();
			BindParent();
			_boundcontrol.DataBind();
			RaiseEvent(DataUpdate,this);
		}
		private void Pager_Click(object sender, System.EventArgs e)
		{
			LinkButton button = (LinkButton) sender;
			CurrentPage = int.Parse(button.ID);
			RaiseEvent(PageChanged, this,new PageChangedEventArgs(CurrentPage,PagedEventInvoker.Pager));
			Update();
		}
		private void Next_Click(object sender, System.Web.UI.ImageClickEventArgs e)
		{
			if (CurrentPage<TotalPagesToShow)
				CurrentPage++;
			RaiseEvent(PageChanged, this,new PageChangedEventArgs(CurrentPage,PagedEventInvoker.Next));
			Update();
		}
		private void Previous_Click(object sender, System.Web.UI.ImageClickEventArgs e)
		{
			if (CurrentPage > 1)
				CurrentPage--;
			RaiseEvent(PageChanged, this,new PageChangedEventArgs(CurrentPage,PagedEventInvoker.Previous));
			Update();
		}
		private void First_Click(object sender, System.Web.UI.ImageClickEventArgs e)
		{
			CurrentPage = 1;
			RaiseEvent(PageChanged, this,new PageChangedEventArgs(CurrentPage,PagedEventInvoker.First));
			Update();
		}
		private void Last_Click(object sender, System.Web.UI.ImageClickEventArgs e)
		{
			CurrentPage = TotalPagesToShow;
			RaiseEvent(PageChanged, this,new PageChangedEventArgs(CurrentPage,PagedEventInvoker.Last));
			Update();
		}
		
	}
		#endregion
}